package com.bupt.secret.service;

import com.bupt.secret.dto.*;
import com.sun.jna.Native;
import com.sun.jna.Library;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
/**
 * @desc 加载c语言的so库，和C的方法声明对应
 * @author shishumin
 * @date 2024/9/3 17:54
 */
public class BuptSecretService {
    // 定义一个接口来访问本地方法
    public interface ServerLibrary extends Library {
        ServerLibrary INSTANCE = Native.load(loadLibraryFromResources("libServer.so"), ServerLibrary.class);
        void printf(String format, Object... args);
    }

    // 定义一个接口来访问本地方法
    public interface GMTLibrary extends Library {
        GMTLibrary INSTANCE =  Native.load(loadLibraryFromResources("libBUPTSDF.so"), GMTLibrary.class);

        /**
         *  功能描述：打开设备，获得设备句柄
         *  参数：phDeviceHandle【out】 返回设备句柄
         *  返回值：0  成功； 非0  失败，返回错误代码
         *  备注： phDeviceHandle 由函数初始化并填写内容
         *  (C语言)int SDF_OpenDevice(void ** phDeviceHandle);
         **/
        int SDF_OpenDevice(long[] phDeviceHandle);

        /**
         *  功能描述：关闭设备，并释放相关资源
         *  参数：hDeviceHandle【in】 设备句柄
         *  返回值：0  成功； 非0  失败，返回错误代码
         *  int SDF_CloseDevice(void *hDeviceHandle);
         **/
        int SDF_CloseDevice(long phDeviceHandle);

        /**
         *  功能描述：关闭会话
         *  参数：hDeviceHandle【in】 已经打开设备句柄
         *  返回值：0  成功； 非0  失败，返回错误代码
         *
         **/
        int SDF_CloseSession(long hSessionHandle);

        /**
         *  功能描述：创建与密码设备的会话
         *  参数：hDeviceHandle【in】 已经打开设备句柄
         *        phDeviceHandle[out] 返回与密码设备建立的 新会话句柄
         *  返回值：0  成功； 非0  失败，返回错误代码
         * (C语言)int SDF_OpenSession(void * hDeviceHandle,void ** phSessionHandle);
         **/
        int SDF_OpenSession(long hDeviceHandle,long[] phSessionHandle);

        /**
         *  功能描述：获取指定长度随机数
         *  参数：hDeviceHandle【in】 已经打开设备句柄
         *        uiLength[in]        欲获取随机数长度
         *        pucRandom[out]      缓冲区指针，用于存储获取的随机数
         *  返回值：0  成功； 非0  失败，返回错误代码
         *  (C语言)int SDF_GenerateRandom(void *hSessionHandle,unsigned int uiLength,unsigned char * pucRandom);
         **/
        int SDF_GenerateRandom(long hSessionHandle, int uiLength, byte[] pucRandom);

        /**
         *  功能描述：获取密码设备能力描述
         *  参数：hDeviceHandle【in】 已经打开设备句柄
         *        pstDeviceInfo[out]  设备能力描述信息
         *  返回值：0  成功； 非0  失败，返回错误代码
         * int SDF_GetDeviceInfo(void * hSessionHandle,DEVICEINFO * pstDeviceInfo);
         **/
        int SDF_GetDeviceInfo(long hSessionHandle, DeviceInfo pstDeviceInfo);

        /**   6.3.1
         *  功能描述：导出密码设备内部存储的指定索引的签名公钥
         *  参数： hSessionHandle【in】 已经打开设备句柄
         *         uiKeyIndex[in]     密码设备存储私钥索引
         *         pucPublicKey[out]  RSA公钥结构
         *  返回值：0  成功； 非0  失败，返回错误代码
         *  备注： 本标准涉及密码设备存储的密钥对索引值的的起始索引值为１，最大为 狀 ，密码设备的实 际存储容量决定 狀 值
         **/
        int SDF_ExportSignPublicKey_RSA(long hSessionHandle, int uiKeyIndex, RSArefPublicKey pucPublicKey);

        /**  6.3.2
         *  功能描述：导出密码设备内部存储的指定索引的加密公钥
         *  参数： hSessionHandle【in】 已经打开设备句柄
         *         uiKeyIndex[in]     密码设备存储私钥索引
         *         pucPublicKey[out]  RSA公钥结构
         *  返回值：0  成功； 非0  失败，返回错误代码
         *
         **/
        int SDF_ExportEncPublicKey_RSA(long hSessionHandle, int uiKeyIndex, RSArefPublicKey pucPublicKey);

        /**  6.3.3.1
         *  功能描述：请求密码设备产生指定模长的RSA密钥对
         *  参数： hSessionHandle【in】 已经打开会话句柄
         *         uiKeyBits【in】      指定密钥模长(1024, 2048)
         *         pucPublicKey[out]    RSA公钥结构
         *         pucPrivateKey[out]   RSA私钥结构
         *  返回值：0  成功； 非0  失败，返回错误代码
         *
         **/
        int SDF_GenerateKeyPair_RSA(long hSessionHandle, int uiKeyBits, RSArefPublicKey pucPublicKey, CipherRSArefPrivateKey pucPrivateKey);

        /**   6.3.3.2
         *  功能描述：生成RSA密钥对,私钥用口令加密输出
         *  参数： hSessionHandle [in]        与设备建立的会话句柄
         *         uiKeyBits      [in]        公钥模长（RSA:1024或者2048）
         *         pin            [in]        口令
         *         pinLen         [in]        口令长度
         *         secertKeyAlgID [in]        保密密钥算法标识
         *         pucPublicKey   [out]       公钥
         *         pucKey         [out]       私钥密文
         *  返回值：0  成功； 非0  失败，返回错误代码
         **/
        int GenerateKeyPair_RSA(long hSessionHandle, int uiKeyBits,byte[] pin,int pinLen, int secertKeyAlgID, RSArefPublicKey pucPublicKey, CipherRSArefPrivateKey pucKey);

        /**   6.3.3.3
         *  功能描述：导入RSA密钥对,私钥已做加密处理
         *  参数： hSessionHandle [in]        与设备建立的会话句柄
         *         uiKeyIndex     [in]        密钥索引（自定义索引，值小于255）(之后在6.3.2   6.3.6接口中使用)
         *         pin            [in]        口令
         *         pinLen         [in]        口令长度
         *         secertKeyAlgID [in]        保密密钥算法标识
         *         pucPublicKey   [in]        公钥
         *         pucKey         [in]       私钥密文
         *  返回值：0  成功； 非0  失败，返回错误代码
         **/
        int ImportKeyPair_RSA(long hSessionHandle, int uiKeyBits,byte[] pin,int pinLen, int secertKeyAlgID, RSArefPublicKey pucPublicKey, CipherRSArefPrivateKey pucKey);

        /**   6.3.4
         *  功能描述：生成会话密钥并用内部RSA公钥加密输出
         *  参数： hSessionHandle【in】 已经打开会话句柄
         *         uiPKIndex【in】      密码设备内部存储公钥索引
         *         uiKeyBits【in】      指定产生会话密钥长度
         *         pucKey[out]          缓冲区指针，用于存放返回的密钥密文
         *         puiKeyLength[out]    返回的密钥密文长度
         *         phKeyHandle[out]     返回的密钥句柄
         *  返回值：0  成功； 非0  失败，返回错误代码
         *  备注： 公钥加密数据时填充方式按照ＰＫＣＳ＃１ｖ １． ５的要求进行
         **/
        int SDF_GenerateKeyWithIPK_RSA(long hSessionHandle, int uiPKIndex, int uiKeyBits,
                                       byte[] pucKey, int[] puiKeyLength, long[] phKeyHandle);

        /**   6.3.5(和6.3.6是对应关系)
         *  功能描述：生成会话密钥并用外部RSA公钥加密输出
         *  参数： hSessionHandle【in】 已经打开设备句柄
         *         uiKeyBits【in】      指定产生会话密钥长度
         *         pucPublicKey【in】   输入的外部RSA公钥结构
         *         pucKey[out]          缓冲区指针，用于存放返回的密钥密文
         *         puiKeyLength[out]    返回的密钥密文长度
         *         phKeyHandle[out]     返回的密钥句柄
         *  返回值：0  成功； 非0  失败，返回错误代码
         *  备注： 公钥加密数据时填充方式按照ＰＫＣＳ＃１ｖ １． ５的要求进行
         **/
        int SDF_GenerateKeyWithEPK_RSA(long hSessionHandle, int uiKeyBits, RSArefPublicKey pucPublicKey,
                                       byte[] pucKey, int[] puiKeyLength, long[] phKeyHandle);

        /**  6.3.6(和6.3.5是对应关系)
         *  功能描述：导入会话密钥并用内部RSA私钥解密
         *  参数： hSessionHandle【in】 已经打开会话句柄
         *         uiISKIndex【in】     密码设备内部存储加密私钥的索引值
         *         pucKey[in]          输入会话密钥
         *         puiKeyLength[in]    密钥密文长度
         *         phKeyHandle[out]     返回的密钥句柄
         *  返回值：0  成功； 非0  失败，返回错误代码
         *  备注： 公钥加密数据时填充方式按照ＰＫＣＳ＃１ｖ １． ５的要求进行
         **/
        int SDF_ImportKeyWithISK_RSA(long hSessionHandle, int uiISKeyIndex, byte[] pucKey, int puiKeyLength,long[] phKeyHandle );

        /**  6.3.8
         *  功能描述：导出密码设备内部存储的指定索引的ECC签名公钥
         *  参数： hSessionHandle【in】 已经打开设备句柄
         *         uiKeyIndex[in]     密码设备存储ECC密钥对索引
         *         pucPublicKey[out]  ECC公钥结构
         *  返回值：0  成功； 非0  失败，返回错误代码
         *
         **/
        int SDF_ExportSignPublicKey_ECC(long hSessionHandle, int uiKeyIndex, ECCrefPublicKey pucPublicKey);

        /**  6.3.9
         *  功能描述：导出密码设备内部存储的指定索引的ECC加密公钥
         *  参数： hSessionHandle【in】 已经打开设备句柄
         *         uiKeyIndex[in]     密码设备存储的ECC密钥对索引
         *         pucPublicKey[out]  ECC公钥结构
         *  返回值：0  成功； 非0  失败，返回错误代码
         *
         **/

        int SDF_ExportEncPublicKey_ECC(long hSessionHandle, int uiKeyIndex, ECCrefPublicKey pucPublicKey);

        /**  6.3.11
         *  功能描述：生成会话密钥并用内部ECC公钥加密输出，同时返回密钥句柄
         *  参数： hSessionHandle【in】 已经打开设备句柄
         *         uiIPKIndex【in】     密码设备内部存储公钥索引
         *         uiKeyBits【in】      指定产生会话密钥长度
         *         pucKey[out]          缓冲区指针，用于存放返回的密钥密文
         *         phKeyHandle[out]     返回的密钥句柄
         *  返回值：0  成功； 非0  失败，返回错误代码
         **/
        int SDF_GenerateKeyWithIPK_ECC(long hSessionHandle, int uiPKIndex, int uiKeyBits,
                                       ECCCipher pucKey, long[] phKeyHandle);

        /**  6.3.12
         *  功能描述：生成会话密钥并用外部ECC公钥加密输出
         *  参数： hSessionHandle【in】 已经打开设备建立的会话句柄
         *         uiKeyBits【in】      指定产生会话密钥长度
         *         uiAlgID【in】        外部ECC公钥的算法标识
         *         pucPublicKey【in】   输入的外部ECC公钥结构
         *         pucKey[out]          缓冲区指针，用于存放返回的密钥密文
         *         phKeyHandle[out]     返回的密钥句柄
         *  返回值：0  成功； 非0  失败，返回错误代码
         *
         **/
        int SDF_GenerateKeyWithEPK_ECC(long hSessionHandle, int uiKeyBits, int uiAlgID,ECCrefPublicKey pucPublicKey,ECCCipher pucKey,long[] phKeyHandle );

        /**  6.3.13
         *  功能描述：导入会话密钥并用内部ECC私钥解密,同时返回密钥句柄
         *  参数： hSessionHandle【in】 已经打开设备建立的会话句柄
         *         uiISKIndex【in】     密码设备内部存储加密私钥的索引值，对应于加密时候的公钥
         *         pucKey[in]           缓冲区指针，用于存放返回的密钥密文
         *         phKeyHandle[out]     返回的密钥句柄
         *  返回值：0  成功； 非0  失败，返回错误代码
         *
         **/

        int SDF_ImportKeyWithISK_ECC(long hSessionHandle, int uiISKeyIndex,ECCCipher pucKey,long[] phKeyHandle);

        /**  6.3.18
         *  功能描述：生成会话密钥，并用密钥加密密钥加密输出
         *  参数： hSessionHandle【in】 已经打开设备会话句柄
         *         uiKeyBits【in】      指定产生的会话密钥长度
         *         uiAlgID[in]          算法标识，指定对称加密算法
         *         uiKEYIndex【in】     密码设备内部存储的密钥加密密钥的索引值
         *         pucKey[out]          缓冲区指针，用于存放输出的会话密钥密文
         *         puiKeyLength[out]    返回的密钥密文长度
         *         phKeyHandle[out]     返回的密钥句柄
         *  返回值：0  成功； 非0  失败，返回错误代码
         *   备注： 加密模式使用ＥＣＢ模式
         **/
        int SDF_GenerateKeyWithKEK(long hSessionHandle, int uiKeyBits, int uiAlgID,
                                   int uiKEKIndex,byte[] pucKey, int[] puiKeyLength,long[] phKeyHandle );

        /**  6.3.19
         *  功能描述：导入会话密钥，并用密钥加密密钥解密，同时返回会话密钥句柄
         *  参数： hSessionHandle【in】 已经打开设备会话句柄
         *         uiAlgID[in]          算法标识，指定对称加密算法
         *         uiKEYIndex【in】     密码设备内部存储的密钥加密密钥的索引值
         *         pucKey[in]           缓冲区指针，用于存放输入的会话密钥密文
         *         puiKeyLength[in]    返回的密钥密文长度
         *         phKeyHandle[out]     返回的密钥句柄
         *  返回值：0  成功； 非0  失败，返回错误代码
         *   备注： 加密模式使用ＥＣＢ模式
         **/

        int SDF_ImportKeyWithKEK(long hSessionHandle, int uiAlgID, int uiKEKIndex,
                                 byte[] pucKey, int puiKeyLength,long[] phKeyHandle );

        /**  6.3.20
         *  功能描述：销毁会话密钥，并释放为密钥句柄分配的内存资源
         *  参数： hSessionHandle【in】 已经打开设备会话句柄
         *         hKeyHandle[in]     返回的密钥句柄
         *  返回值：0  成功； 非0  失败，返回错误代码
         *  备注： 在对称算法运算完成后，应调用本函数销毁会话密钥
         **/

        int SDF_DestroyKey(long hSessionHandle, long hKeyHandle);

        /////////////////////////////////////////////////////////////////////////////////////

        /**   6.4.1
         *  功能描述：指定使用外部公钥RSA对数据进行运算
         *  参数： hSessionHandle【in】      与设备建立的会话句柄
         *         pucPublicKey[in]          外部RSA公钥结构
         *         pucDataInput[in]          缓冲器指针，用于存放输入的数据
         *         uiInputLength[in]         输入数据长度
         *         pucDataOutput[out]        缓冲器指针，用于存放输出的数据
         *         puiOutputLength[out]      输出数据长度
         *  返回值：0  成功； 非0  失败，返回错误代码
         *  备注： 数据格式由应用层封装
         **/
        int SDF_ExternalPublicKeyOperation_RSA(long hSessionHandle,RSArefPublicKey  pucPublicKey,byte[] pucDataInput,
                                               int uiInputLength,byte[] pucDataOutput,int[] puiOutputLength );

        /**   6.4.3
         *  功能描述：指定使用内部指定RSA私钥对数据进行运算
         *  参数： hSessionHandle【in】      与设备建立的会话句柄
         *         uiKeyIndex[in]            密码设备内部存储私钥的索引值(RSA:1)
         *         pucDataInput[in]          缓冲器指针，用于存放输入的数据
         *         uiInputLength[in]         输入数据长度
         *         pucDataOutput[out]        缓冲器指针，用于存放输出的数据
         *         puiOutputLength[out]      输出数据长度
         *  返回值：0  成功； 非0  失败，返回错误代码
         *  备注：  索引范围仅限于内部签名密钥对，数据格式由应用层封装
         **/
        int SDF_InternalPrivateKeyOperation_RSA(long hSessionHandle, int uiKeyIndex,byte[] pucDataInput, int uiInputLength,
                                                byte[] pucDataOutput,int[] puiOutputLength);

        // 对称加解密和HMAC
        /////////////////////////////////////////////////////////////////////////////////////
        /**   6.5.1
         *  功能描述：使用指定的密钥句柄和IV对数据进行运算
         *  参数： hSessionHandle【in】      与设备建立的会话句柄
         *         hKeyHandle[in]            指定密钥句柄
         *         uiAlgID[in]               算法标识，指定对称加密算法
         *         pucIV[in|out]             缓冲区指针，用于存放输入和输出的IV向量（16字节）
         *         pucData[in]               缓冲器指针，用于存放输入的明文数据
         *         uiDataLength[in]          输入数据长度
         *         pucEncData[out]           缓冲器指针，用于存放输出的数据密文
         *         puiEncDataLength[out]     输出数据长度
         *  返回值：0  成功； 非0  失败，返回错误代码
         *  备注： 此函数不对数据进行填充处理，输入的数据必须是指定算法分组长度的整数倍。
         **/
        int SDF_Encrypt(long hSessionHandle,long hKeyHandle, int uiAlgID, byte[] pucIV,
                        byte[] pucData, int uiDataLength, byte[] pucEncData, int[] puiEncDataLength);

        /**   6.5.2
         *  功能描述：使用指定的密钥句柄和IV对数据进行对称解密运算
         *  参数： hSessionHandle【in】             与设备建立的会话句柄
         *             hKeyHandle[in ]            指定的密钥句柄
         *             uiAlgID[in]                算法标识，指定对称加密算法
         *             pucIV[in|out]              缓冲区指针，用于存放输入和返回的IV 数据
         *             pucEncData[in]             缓冲区指针，用于存放输入的数据密文
         *             uiEncDataLength[in]        输入的数据密文长度
         *             pucData[out]               缓冲区指针用于存放输出的数据明文
         *             puiDataLength [out]        输出的数据明文长度
         *  返回值：0  成功； 非0  失败，返回错误代码
         *  备注： 此函数不对数据进行填充处理，输入的数据必须是指定算法分组长度的整数倍。
         **/
        int SDF_Decrypt(long hSessionHandle,long hKeyHandle, int uiAlgID, byte[] pucIV,
                        byte[] pucData, int uiDataLength,byte[] pucEncData, int[] puiEncDataLength );
        /////////////////////////////////////////////////////////////////////////////////////

    }

    /**
     * @desc 从 resources 目录中加载.SO库
     * @author shishumin
     * @date 2024/8/30 14:50
     */
    private static String loadLibraryFromResources(String fileName) {
        try {
            // 获取资源的输入流
//            InputStream in = BuptSecretServiceUtil.class.getClassLoader().getResourceAsStream("libtest.dylib");
            System.out.println("fileName:" + fileName);
            InputStream in = BuptSecretService.class.getClassLoader().getResourceAsStream(fileName);
            if (in == null) {
                throw new RuntimeException("Library file not found in resources.");
            }

            // 创建临时文件
            int dotIndex = fileName.lastIndexOf(".");
            String name = fileName.substring(0, dotIndex);
            // 获取文件的扩展名
            String extension = fileName.substring(dotIndex);

            System.out.println("extension:" + extension);
            File tempFile = Files.createTempFile(name, extension).toFile();

            System.out.println("tempFile:" + tempFile.getPath());
            tempFile.deleteOnExit(); // 确保在 JVM 退出时删除临时文件

            // 将资源流写入临时文件
            try (OutputStream out = new FileOutputStream(tempFile)) {
                byte[] buffer = new byte[1024];
                int length;
                while ((length = in.read(buffer)) > 0) {
                    out.write(buffer, 0, length);
                }
            }
            System.out.println("getAbsolutePath:" + tempFile.getAbsolutePath());
            // 返回临时文件的绝对路径
            return tempFile.getAbsolutePath();
        } catch (Exception e) {
            throw new RuntimeException("Failed to load library from resources.", e);
        }
    }
    
}
